﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CodeGenerator.CSharp.Extensions;
using System.Threading.Tasks;


namespace CodeGenerator.CSharp
{
	public class FileBuilder : CodeUnit
	{
		#region Constructors

        public FileBuilder()
        {

        }

		/// <summary>
		/// Base class constructor.
		/// </summary>
		/// <param name="fileName">The name of the file.</param>
		/// <param name="directory">The directory where this file should be written to disk.</param>
        public FileBuilder(string projectName, string directory, string fileName, string thisNamespace)
			: base(fileName) 
		{
            this.ProjectName = projectName;
            this.Directory = directory;
            this.FileName = fileName;
            this.Namespace = thisNamespace;
		}

        #endregion

		#region Creating/Created Events

		/// <summary>
		/// Indicates that we are about to create a specific file.
		/// </summary>
		public static event EventHandler<FileEventArgs> Creating;
		private void RaiseCreating()
		{
			if (this.RaiseEvents && FileBuilder.Creating.IsSomething()) 
				FileBuilder.Creating(this, new FileEventArgs(this));
		}

		/// <summary>
		/// Indicates that we are have created a specific file.
		/// </summary>
		public static event EventHandler<FileEventArgs> Created;
		private void RaiseCreated()
		{
			if (this.RaiseEvents && FileBuilder.Created.IsSomething())
				FileBuilder.Created(this, new FileEventArgs(this));
		}

		#endregion

		#region Editing/Edited Events

		/// <summary>
		/// Indicates that we are about to edit a specific file.
		/// </summary>
		public static event EventHandler<FileEventArgs> Editing;
		private void RaiseEditing()
		{
			if (this.RaiseEvents && FileBuilder.Editing.IsSomething())
				FileBuilder.Editing(this, new FileEventArgs(this));
		}

		/// <summary>
		/// Indicates that we are have edited a specific file.
		/// </summary>
		public static event EventHandler<FileEventArgs> Edited;
		private void RaiseEdited()
		{
			if (this.RaiseEvents && FileBuilder.Edited.IsSomething())
				FileBuilder.Edited(this, new FileEventArgs(this));
		}

		#endregion

		#region Deleting/Deleted Events
		
		/// <summary>
		/// Indicates that we are about to delete a specific file.
		/// </summary>
		public static event EventHandler<FileEventArgs> Deleting;
		private void RaiseDeleting()
		{
			if (this.RaiseEvents && FileBuilder.Deleting.IsSomething())
				FileBuilder.Deleting(this, new FileEventArgs(this));
		}

		/// <summary>
		/// Indicates that we are have deleted a specific file.
		/// </summary>
		public static event EventHandler<FileEventArgs> Deleted;
		private void RaiseDeleted()
		{
			if (this.RaiseEvents && FileBuilder.Deleted.IsSomething())
				FileBuilder.Deleted(this, new FileEventArgs(this));
		}
		
		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets the name of this file.
		/// </summary>
		public string FileName { get; set; }

		/// <summary>
		/// Gets or sets the directory for this file.
		/// </summary>
		public string Directory { get; set; }

		/// <summary>
		/// Gets or sets the namespace for this file.
		/// </summary>
		public string Namespace { get; set; }

		/// <summary>
		/// Gets the fully qualified name of this file, including its directory.
		/// </summary>
		public string FullName { get { return System.IO.Path.Combine(this.Directory, this.FileName); } }

		/// <summary>
		/// Identifies if this file exists on disk.
		/// </summary>
		public bool Exists { get { return System.IO.File.Exists(this.FullName); } }

		/// <summary>
		/// Gets or sets the name of the Visual Studio project that this file belongs to.
		/// </summary>
		public string ProjectName { get; set; }

        /// <summary>
        /// Gets or sets this file is auto-generated.  If so, a message will be added to the top
        /// of this file warning developers not to modify this file.
        /// </summary>
        public bool IsAutoGenerated { get; set; }

		/// <summary>
		/// Gets or sets if events defined by this class (e.g., Creating, Created, Editing, etc.) should
		/// be raised.
		/// </summary>
		public bool RaiseEvents { get; set; }

		/// <summary>
		/// Identifies the list of namespaces to use in this file.
		/// </summary>
		public List<string> UsingNamespaces { get{ return this._UsingNamespaces;} }
		private List<string> _UsingNamespaces = new List<string>();

        /// <summary>
        /// Identifies the list of namespaces to use in this file.
        /// </summary>
        public List<string> UsingNonSilverlightNamespaces { get { return this._UsingNonSilverlightNamespaces; } }
        private List<string> _UsingNonSilverlightNamespaces = new List<string>();

		#endregion

		#region WriteToDisk

		/// <summary>
		/// Writes the file defined by this object to disk.
		/// </summary>
		/// <param name="overwrite">Identifies if the file should be overwritten if it already exists.</param>
		public void WriteToDisk(OverwriteExistingFile overwrite)
		{
			// We will not overwrite a file that already exists unless we have been asked to do so.
			if (this.Exists && overwrite == OverwriteExistingFile.No) return;

			try
			{
				// Provide notification that we are editing/creating this file.
				if (this.Exists) this.RaiseEditing(); else this.RaiseCreating();

				// Create the directory for this file if it does not already exist.
				System.IO.FileInfo ThisFileInfo = new System.IO.FileInfo(this.FullName);
				if (!System.IO.Directory.Exists(ThisFileInfo.Directory.FullName)) System.IO.Directory.CreateDirectory(ThisFileInfo.Directory.FullName);

				// Write this file to disk.
				System.IO.File.WriteAllText(this.FullName, this.ToString());

				// Provide notification that we edited/created this file.
				if (this.Exists) this.RaiseEdited(); else this.RaiseCreated();
			}
			catch (Exception ex)
			{
				throw new Exception("Unexpected problem trying to generate file: " + this.FullName, ex);
			}
		}


        public Task WriteToDiskAsync(OverwriteExistingFile overwrite)
        {
            var ThisTask = new Task(delegate()
            {
                this.WriteToDisk(overwrite);
            });
            ThisTask.Start();
            return ThisTask;
        }

		#endregion

		#region Delete

		/// <summary>
		/// Deletes this file from disk. (If the file does not exists, nothing will happen.)
		/// </summary>
		public void Delete()
		{
			if (!this.Exists) return;
			try
			{
				this.RaiseDeleting();
				System.IO.File.Delete(this.FullName);
				this.RaiseDeleted();
			}
			catch (Exception ex)
			{
				throw new Exception("Unexpected problem trying to delete file: " + this.FullName, ex);
			}
		}

		#endregion

        #region ToString
        public override string ToString()
		{
            try
            {
			    CodeBuilder ThisCodeBuilder = new CodeBuilder();

                if (this.IsAutoGenerated)
                {
                    ThisCodeBuilder.Add("// ----------------------------- WARNING -----------------------------");
                    ThisCodeBuilder.Add("// Note that this file is auto-generated - DO NOT MODIFY THIS FILE");
                    ThisCodeBuilder.Add("// as any changes will be lost the next time this file is generated.");
                    ThisCodeBuilder.Add("// -------------------------------------------------------------------");
                    ThisCodeBuilder.Add();
                }

			    // Imported namespaces.
			    foreach (string ThisNamespace in this._UsingNamespaces)
				    ThisCodeBuilder.Add("using " + ThisNamespace + ";");
			    if (this._UsingNamespaces.Count > 0) ThisCodeBuilder.Add();

                //if (this._UsingNonSilverlightNamespaces.Count > 0)
                //{
                //    ThisCodeBuilder.Add("#if !SILVERLIGHT");
                //    foreach (string ThisNamespace in this._UsingNonSilverlightNamespaces)
                //        ThisCodeBuilder.Add("using " + ThisNamespace + ";");
                //    ThisCodeBuilder.Add("#endif");
                //    ThisCodeBuilder.Add();
                //}

                var thisNamespace = new Namespace(this.Namespace);
                thisNamespace.CodeUnits.Add(this.CodeUnits);
                ThisCodeBuilder.Add(thisNamespace.ToString());
			    return ThisCodeBuilder.ToString();
            }
            catch (Exception ex)
            {
                
                throw ex;
            }

		} 
		#endregion

	}


	/// <summary>
	/// Provides file information for events.
	/// </summary>
	public class FileEventArgs : EventArgs
	{
		internal FileEventArgs(FileBuilder thisFile)
		{
			this.FileName = thisFile.FileName;
			this.FullName = thisFile.FullName;
			this.ProjectName = thisFile.ProjectName;
		}
		public string FullName { get; private set; }
		public string ProjectName { get; private set; }
		public string FileName { get; private set; }
		public string SubFolder { get { return System.IO.Directory.GetParent(this.FullName).Name; } }

	}

	/// <summary>
	/// Indicates if a file should be overwritten if it already exists.
	/// </summary>
	public enum OverwriteExistingFile { Yes, No }

}
